package com.neborosoft.lib.collections.tree;

import com.neborosoft.lib.API_8.Collections;
import com.neborosoft.lib.threading.AbstractAsyncTask;
import com.neborosoft.lib.threading.OnHostThreadExecutor;

import java.util.List;
import java.util.concurrent.Executor;

/**
 * Created with IntelliJ IDEA.
 * User: CM
 * Date: 02.02.13
 * Time: 0:07
 * To change this template use File | Settings | File Templates.
 */
public abstract class AsyncNTreeWrapper {
    private NTree tree;
    private Executor executor;
    private OnHostThreadExecutor onHostThreadExecutor;
    private volatile boolean childEnteringExecuted = false;
    private volatile int levelBeforeChildEnteringExecuted = -1;
    private volatile Object parentBeforeChildEnteringExecuted;
    private volatile int stepsCountForChildEntering;

    private AbstractAsyncTask<Integer,Object> treeTask;

    public NTree getTree() {
        return tree;
    }

    public void setTree(NTree tree) {
        if(tree == null) {
            throw new NullPointerException();
        }

        this.tree = tree;
    }

    protected AsyncNTreeWrapper(final NTree tree,
                                Executor executor,
                                OnHostThreadExecutor onHostThreadExecutor)
    {
        setTree(tree);
        this.executor = executor;
        this.onHostThreadExecutor = onHostThreadExecutor;

        treeTask = new AbstractAsyncTask<Integer, Object>(onHostThreadExecutor){
            @Override
            protected void onPostExecute(Object o) {
                update();
            }

            @Override
            protected Object doInBackground(Integer position) {
                tree.enterChild(position);
                childEnteringExecuted = false;
                return null;
            }
        };
    }

    protected abstract void onElementsUpdated(int level, List elements);
    protected abstract boolean shouldEnterChildAsync(Object parent, int level);

    private boolean shouldEnterChildAsync(int position){
        int level = tree.getLevel();
        List elements = tree.getElements();
        Object parent = elements.get(position);
        return shouldEnterChildAsync(parent, level);
    }

    private void update(){
        int level = tree.getLevel();
        List elements = tree.getElements();
        onElementsUpdated(level, elements);
    }

    public void enterChild(final int position){
        if (childEnteringExecuted) {
            throw new IllegalStateException();
        }

        if(!shouldEnterChildAsync(position)){
            tree.enterChild(position);
            return;
        }

        childEnteringExecuted = true;
        levelBeforeChildEnteringExecuted = tree.getLevel();
        parentBeforeChildEnteringExecuted = tree.getElements().get(position);
        stepsCountForChildEntering = tree.getLevelStepsCountFromParentToChild(
                parentBeforeChildEnteringExecuted, levelBeforeChildEnteringExecuted);

        treeTask.execute(position, executor);
    }

    public void exitChild(){
        tree.exitChild();
    }

    public void exitToLevel(int level){
        tree.exitToLevel(level);
    }

    public boolean canExitChild(){
        return tree.canEnterChild();
    }

    public boolean canEnterChild(){
        return tree.canEnterChild();
    }

    public List getElements(){
        if (childEnteringExecuted) {
            return Collections.emptyList();
        } else {
            return tree.getElements();
        }
    }

    public int getLevel(){
        if(childEnteringExecuted){
            int offset = stepsCountForChildEntering + 1;
            return levelBeforeChildEnteringExecuted + offset;
        } else {
            return tree.getLevel();
        }
    }

    public int getLevelsCount(){
        return tree.getLevelsCount();
    }

    public Object getParent(){
        if(!childEnteringExecuted){
            return tree.getParent();
        } else {
            return parentBeforeChildEnteringExecuted;
        }
    }

    public void restart(){
        tree.restart();
    }

    public void restart(List rootItems){
        tree.restart(rootItems);
    }

    public NTree.State getState(){
        return tree.getState();
    }

    public void restoreState(NTree.State state){
        tree.restoreState(state);
    }
}
